/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: registration_center_service_finder.cpp
 *
 * Purpose: implementation a service finder by registration center client
 *
 * Developer:
 *   wen.gu , 2021-09-30
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "registration_center_service_finder.h"
#include "icpp/com/message_broker_simple.h"


#define LOG_TAG "rcsf"
#include "icpp/core/log.h"
namespace icpp
{
namespace com
{
/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
RegistrationCenterServiceFinder::RegistrationCenterServiceFinder(const std::string& srv_name)
    :srv_name_(srv_name)
{
    /** todo something */
}

RegistrationCenterServiceFinder::~RegistrationCenterServiceFinder()
{
    /** todo something */
}

/**
 * try to find a service instance, and return result immediately.
*/
Result<ServiceInfo> RegistrationCenterServiceFinder::tryFindService() 
{
    Response<ServiceInfo> res = RegistrationCenterClient::getInstance().getServiceInfo(srv_name_);
    return res.getResult(); /** todo refineme this maybe dead block */
}

/**
 * start to find a service instance and wait unitil timeout or successfull find a valid service instance
 */
Result<ServiceInfo> RegistrationCenterServiceFinder::findService(uint32_t wait_ms /*= FIND_SERVICE_WAIT_INFINITE*/)
{
    core::AutoLock al(lock_);

    if (is_wait_)
    {
        return Result<ServiceInfo>::FromError(IcppErrc::InvalidStatus);        
    }

    is_wait_ = true;
    res_builder_for_wait_ = std::make_shared< ResponseBuilder<ServiceInfo> >();
    ResponseBuilder<ServiceInfo>::ResponseBuilderPtr res_buidler_ptr = res_builder_for_wait_;
    ResponseBuilder<ServiceInfo>* res_buidler = res_buidler_ptr.get();  

    Response<uint32_t> listener_res = RegistrationCenterClient::getInstance().addListener(srv_name_, [res_buidler_ptr](ServiceActivityStatus status, const ServiceInfo& srv_info)
    {
        if (status == ServiceActivityStatus::kOnline)
        {
            res_buidler_ptr->setValue(srv_info);
        }        
    });

    Result<uint32_t> listener_rt = listener_res.getResult(); /** todo, maybe dead block?? */

    if (listener_rt.hasValue() == false)
    {
        return Result<ServiceInfo>::FromError(listener_rt.error());   
    }

    listener_id_for_wait_ = listener_rt.value();

    Response<ServiceInfo> res = res_buidler->getResponse();

    if (wait_ms <= GET_SERVICE_WAIT_INFINITE)
    {
        res.wait();
        is_wait_ = false;
        return res.getResult();
    }

    if (res.waitFor(std::chrono::milliseconds(wait_ms)) == ResponseStatus::kReady)
    {
        is_wait_ = false;
        return res.getResult();
    }

    is_wait_ = false;
    return Result<ServiceInfo>::FromError(IcppErrc::Timeout);
}

/**
 * cancel find service  when 'findService' method called and in waiting state. 
 */
Result<void> RegistrationCenterServiceFinder::cancelFindService() 
{
    core::AutoLock al(lock_);

    if (is_wait_ == false)
    {
        return Result<void>::FromError(IcppErrc::InvalidStatus);        
    }    

    RegistrationCenterClient::getInstance().removeListener(srv_name_, listener_id_for_wait_);
    res_builder_for_wait_->setError(IcppErrc::OperationCanceled);
    listener_id_for_wait_ = INVALID_LISTENER_ID;
    is_wait_ = false;

    return Result<void>::FromValue();
}

/**
 * start to find a service with async mode, the result will return by callback  'handler'
 */
Result<void> RegistrationCenterServiceFinder::startFindServiceAsync(OnFindServiceHandler handler) 
{
    if (handler == nullptr)
    {
        return Result<void>::FromError(IcppErrc::BadParameter);
    }

    core::AutoLock al(lock_);

    if (listener_id_for_async_ != INVALID_LISTENER_ID)
    {
        return Result<void>::FromError(IcppErrc::InvalidStatus);
    }

    Response<uint32_t> listener_res = RegistrationCenterClient::getInstance().addListener(srv_name_, [handler](ServiceActivityStatus status, const ServiceInfo& srv_info)
    {
        if (status == ServiceActivityStatus::kOnline)
        {
            handler(srv_info);
        }
    });

    Result<uint32_t> listener_rt = listener_res.getResult(); /** todo, maybe dead block?? */

    if (listener_rt.hasValue() == false)
    {
        return Result<void>::FromError(listener_rt.error());   
    }

    listener_id_for_async_ = listener_rt.value();    

    return Result<void>::FromValue();
}

/**
 * stop to find a service with async mode
 */   
Result<void> RegistrationCenterServiceFinder::stopFindServiceAsync() 
{
    core::AutoLock al(lock_);

    if (listener_id_for_async_ == INVALID_LISTENER_ID)
    {
        return Result<void>::FromError(IcppErrc::InvalidStatus);
    }

    Response<void> listener_res = RegistrationCenterClient::getInstance().removeListener(srv_name_, listener_id_for_async_);

    listener_id_for_async_ = INVALID_LISTENER_ID;

    return listener_res.getResult(); /** todo, maybe dead block?? */
}


} /** namespace com */
} /** namespace icpp */
